/*
 *   Copyright 2006 Wei Lian <lianwei2003.student@sina.com>
 *
 *   This program is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU General Public License
 *   as published by the Free Software Foundation; either version 2
 *   of the License, or (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include <stdio.h>		/* for printf and friends */
#include <stdlib.h>		/* for exit */
#include <unistd.h>		/* for write, pipe */
#include <fcntl.h>		/* for fcntl, O_NONBLOCK */
#include <signal.h>		/* for signal */
#include <gtk/gtk.h>		/* well guess for what ... */
#include <sys/wait.h>
#include <sys/ptrace.h>
static int signal_pipe[2];
static int sound_handler;
static pid_t traced_process;
guint ptimer;
int detachflag;
int tracing = 0;
char newurl[64];
pid_t pidplayer;
GtkTextBuffer *buffer;
GtkTextIter cursor;
GtkWidget *entrystatus;
GtkWidget *entryplayer;
GtkWidget *labelplayer;
GtkWidget *buttonplayer;
GtkObject *adj_sound;

gint
timerOut ()
{
  ////sound volume
  int value = sound_read (sound_handler);
  if (gtk_adjustment_get_value ((GtkAdjustment *) adj_sound) != value)
    gtk_adjustment_set_value ((GtkAdjustment *) adj_sound, value);

  ////player
  if ((pidplayer == -1) && GTK_TOGGLE_BUTTON (buttonplayer)->active)
    startplayer ();

  return TRUE;
}

void
onadjustmentchanged (GtkAdjustment * adj)
{
  sound_write (sound_handler, (int) adj->value);
}

void
onbuttonplayertoggled (GtkWidget * widget, gpointer data)
{
  if (GTK_TOGGLE_BUTTON (widget)->active)
    {
      killplayer (pidplayer);
      startplayer ();
    }
  else
    killplayer (pidplayer);
}

void
onbuttonattachtoggled (GtkWidget * widget, gpointer data)
{
  if (GTK_TOGGLE_BUTTON (widget)->active)	////attach
    {
      ///////////////hook process
      traced_process = find_pid_by_name ("Win32Client.exe", "-r");
      if (traced_process == 0)
	{
	  gtk_entry_set_text (GTK_ENTRY (entrystatus),
			      "err, no feidian process");
	}
      else
	{
	  detachflag = 0;
	  if (ptrace
	      ((enum __ptrace_request) PTRACE_ATTACH, traced_process, NULL,
	       NULL) == -1)
	    perror ("ptrace attach");
	  else
	    {
	      tracing = 1;
	      gtk_entry_set_text (GTK_ENTRY (entrystatus), "attached");
	    }
	}
    }
  else				////detach
    detachflag = 1;
}

void
destroy (GtkWidget * widget, gpointer data)
{
  if (tracing)
    detachflag = 2;
  else
    {
      g_source_remove (ptimer);
      killplayer (pidplayer);
      gtk_main_quit ();
    }
}

void
pipe_signals (int signal)
{
  if (write (signal_pipe[1], &signal, sizeof (int)) != sizeof (int))
    {
      fprintf (stderr, "unix signal %d lost\n", signal);
    }
}

gboolean
deliver_signal (GIOChannel * source, GIOCondition cond, gpointer d)
{
  GError *error = NULL;		/* for error handling */
  union
  {
    gchar chars[sizeof (int)];
    int signal;
  } buf;
  GIOStatus status;		/* save the reading status */
  gsize bytes_read;		/* save the number of chars read */
  int waitstat;
  int waitresult;
  while ((status = g_io_channel_read_chars (source, buf.chars,
					    sizeof (int), &bytes_read,
					    &error)) == G_IO_STATUS_NORMAL)
    {
      g_assert (error == NULL);	/* no error if reading returns normal */
      if (bytes_read != sizeof (int))
	{
	  fprintf (stderr,
		   "lost data in signal pipe (expected %d, received %d)\n",
		   sizeof (int), bytes_read);
	  continue;		/* discard the garbage and keep fingers crossed */
	}
      /////////////
      while (1)
	{
	  if ((waitresult =
	       waitpid (WAIT_ANY, &waitstat, WNOHANG | WUNTRACED)) <= 0)
	    break;
	  processSignal (waitresult, waitstat, traced_process);
	}
      /////////////
    }

  if (error != NULL)
    {
      fprintf (stderr, "reading signal pipe failed: %s\n", error->message);
      exit (1);
    }
  if (status == G_IO_STATUS_EOF)
    {
      fprintf (stderr, "signal pipe has been closed\n");
      exit (1);
    }

  g_assert (status == G_IO_STATUS_AGAIN);
  return (TRUE);		/* keep the event source */
}


int
main (int argc, char *argv[])
{
  gtk_init (&argc, &argv);
  GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy), NULL);
  gtk_container_set_border_width (GTK_CONTAINER (window), 3);
  gtk_widget_set_usize (GTK_WIDGET (window), 350, 300);
  GtkWidget *vbox = gtk_vbox_new (FALSE, 0);
  gtk_container_add (GTK_CONTAINER (window), vbox);
  gtk_widget_show (vbox);
  GtkWidget *hbox = gtk_hbox_new (FALSE, 0);
  gtk_widget_show (hbox);
  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
  GtkWidget *label = gtk_label_new ("Player");
  gtk_widget_show (label);
  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
  entryplayer = gtk_entry_new ();
  gtk_widget_show (entryplayer);
  gtk_box_pack_start (GTK_BOX (hbox), entryplayer, TRUE, TRUE, 0);
  gtk_entry_set_text (GTK_ENTRY (entryplayer),
		      "mplayer -ontop -geometry 100%:100%");
  hbox = gtk_hbox_new (FALSE, 0);
  gtk_widget_show (hbox);
  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0);
  GtkWidget *buttonattach = gtk_toggle_button_new_with_label ("Attach");
  g_signal_connect (G_OBJECT (buttonattach), "toggled",
		    G_CALLBACK (onbuttonattachtoggled), NULL);
  gtk_box_pack_start (GTK_BOX (hbox), buttonattach, FALSE, FALSE, 0);
  gtk_widget_show (buttonattach);
  buttonplayer = gtk_toggle_button_new ();
  labelplayer = gtk_label_new ("Player");
  gtk_widget_show (labelplayer);
  gtk_container_add (GTK_CONTAINER (buttonplayer), labelplayer);
  g_signal_connect (G_OBJECT (buttonplayer), "toggled",
		    G_CALLBACK (onbuttonplayertoggled), NULL);
  gtk_box_pack_start (GTK_BOX (hbox), buttonplayer, FALSE, FALSE, 0);
  gtk_widget_show (buttonplayer);
  /////////open mixer
  if ((sound_handler = open ("/dev/mixer", O_RDWR, 0)) == -1)
    {
      perror ("/dev/mixer");
      return -1;
    }
  adj_sound =
    gtk_adjustment_new (sound_read (sound_handler), 0, 100, 1, 0, 0);
  gtk_signal_connect (GTK_OBJECT (adj_sound), "value_changed",
		      GTK_SIGNAL_FUNC (onadjustmentchanged), NULL);
  GtkWidget *hscale_sound = gtk_hscale_new (GTK_ADJUSTMENT (adj_sound));
  gtk_widget_show (hscale_sound);
  gtk_box_pack_start (GTK_BOX (hbox), hscale_sound, TRUE, TRUE, 0);
  gtk_scale_set_digits (GTK_SCALE (hscale_sound), 0);
  GtkWidget *scrolledwindow = gtk_scrolled_window_new (NULL, NULL);
  gtk_widget_show (scrolledwindow);
  gtk_box_pack_start (GTK_BOX (vbox), scrolledwindow, TRUE, TRUE, 0);
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow),
				  GTK_POLICY_NEVER, GTK_POLICY_NEVER);
  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow),
				       GTK_SHADOW_IN);
  GtkWidget *textview = gtk_text_view_new ();
  gtk_widget_show (textview);
  gtk_container_add (GTK_CONTAINER (scrolledwindow), textview);
  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (textview));
  entrystatus = gtk_entry_new ();
  gtk_widget_show (entrystatus);
  gtk_box_pack_start (GTK_BOX (vbox), entrystatus, FALSE, FALSE, 0);
  gtk_widget_show (window);
  ///////////////
  ptimer = g_timeout_add (3000, timerOut, NULL);
  //////////////
  GIOChannel *g_signal_in;
  GError *error = NULL;		/* handle errors */
  long fd_flags;		/* used to change the pipe into non-blocking mode */
  ///////////////pipe
  if (pipe (signal_pipe))
    {
      perror ("pipe");
      exit (1);
    }

  fd_flags = fcntl (signal_pipe[1], F_GETFL);
  if (fd_flags == -1)
    {
      perror ("read descriptor flags");
      exit (1);
    }
  if (fcntl (signal_pipe[1], F_SETFL, fd_flags | O_NONBLOCK) == -1)
    {
      perror ("write descriptor flags");
      exit (1);
    }

  /////////install signal handler
  signal (SIGCHLD, pipe_signals);
  //////////////setup io channel
  g_signal_in = g_io_channel_unix_new (signal_pipe[0]);
  g_io_channel_set_encoding (g_signal_in, NULL, &error);
  if (error != NULL)
    {				/* handle potential errors */
      fprintf (stderr, "g_io_channel_set_encoding failed %s\n",
	       error->message);
      exit (1);
    }

  g_io_channel_set_flags (g_signal_in,
			  g_io_channel_get_flags (g_signal_in) |
			  G_IO_FLAG_NONBLOCK, &error);
  if (error != NULL)
    {				/* tread errors */
      fprintf (stderr, "g_io_set_flags failed %s\n", error->message);
      exit (1);
    }

  g_io_add_watch (g_signal_in, G_IO_IN | G_IO_PRI, deliver_signal, NULL);
  //////////assign initial value
  pidplayer = -1;
  strcpy (newurl, "http://121.1.1.1:1000/live.asf");
  //////main loop
  gtk_main ();
  return 0;
}
